package com.yuven.appframework.util;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Process;
import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.yuven.baselib.utils.Logger;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class CrashHandler implements Thread.UncaughtExceptionHandler {
    private static final CrashHandler INSTANCE = new CrashHandler();

    private Context mContext;
    private String logPath;
    private Thread.UncaughtExceptionHandler mDefaultExceptionHandler;

    @Override
    public void uncaughtException(@NonNull Thread t, @NonNull Throwable e) {
        dumpToSDCard(e);//dump trace 信息到sd卡
        e.printStackTrace();
        if (mDefaultExceptionHandler != null) { //交给系统的UncaughtExceptionHandler处理
            mDefaultExceptionHandler.uncaughtException(t, e);
        } else {
            Process.killProcess(Process.myPid()); //主动杀死进程
        }
    }

    private CrashHandler() {
    }

    public static CrashHandler getInstance() {
        return INSTANCE;
    }

    public void init(Context context, String logPath) {
        this.mContext = context;
        this.logPath = logPath;
        mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();//获取当前默认ExceptionHandler，保存在全局对象
        Thread.setDefaultUncaughtExceptionHandler(this);//替换默认对象为当前对象
    }

    private void dumpToSDCard(final Throwable e) {
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            Logger.i("no sdcard skip dump ");
            return;
        }
        if (TextUtils.isEmpty(logPath)) {
            Logger.i("no log path skip dump ");
            return;
        }
        File file = new File(logPath);
        if (!file.exists()) {
            file.mkdirs();
        }

        File logFile = new File(logPath, "crash_log.txt");
        if (logFile.exists() && logFile.length() > 5 * 1024 * 1024) {
            logFile.delete();
        }

        try {
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(logFile, true)));
            String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(new Date(System.currentTimeMillis()));
            pw.println(time);
            dumpPhoneInfo(pw);
            pw.println();
            e.printStackTrace(pw);

            pw.println("============END=============");
            pw.println();

            pw.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
    }

    private void dumpPhoneInfo(PrintWriter pw) {
        //应用的版本名称和版本号
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi;
        try {
            pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                pw.print("App Version: ");
                pw.print(pi.versionName);
                pw.print('_');
                pw.println(pi.versionCode);

                //android版本号
                pw.print("OS Version: ");
                pw.print(Build.VERSION.RELEASE);
                pw.print("_");
                pw.println(Build.VERSION.SDK_INT);

                //手机制造商
                pw.print("Vendor: ");
                pw.println(Build.MANUFACTURER);

                //手机型号
                pw.print("Model: ");
                pw.println(Build.MODEL);

                //cpu架构
                pw.print("CPU ABI: ");
                pw.println(Build.CPU_ABI);
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }

    //压缩文件夹，为上传做准备。节省流量。
    private void zip(String src, String dest) throws IOException {
        ZipOutputStream out = null;
        File outFile = new File(dest);
        File fileOrDirectory = new File(src);
        out = new ZipOutputStream(new FileOutputStream(outFile));
        if (fileOrDirectory.isFile()) {
            zipFileOrDirectory(out, fileOrDirectory, "");
        } else {
            File[] entries = fileOrDirectory.listFiles();
            if (null != entries) {
                for (File entry : entries) {
                    zipFileOrDirectory(out, entry, "");
                }
            }
        }
        out.close();
    }

    private static void zipFileOrDirectory(ZipOutputStream out, File fileOrDirectory, String curPath) throws IOException {
        FileInputStream in = null;
        if (!fileOrDirectory.isDirectory()) {
            byte[] buffer = new byte[4096];
            int bytes_read;
            in = new FileInputStream(fileOrDirectory);
            ZipEntry entry = new ZipEntry(curPath + fileOrDirectory.getName());
            out.putNextEntry(entry);
            while ((bytes_read = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytes_read);
            }
            out.closeEntry();
        } else {
            File[] entries = fileOrDirectory.listFiles();
            if (null != entries) {
                for (File entry : entries) {
                    zipFileOrDirectory(out, entry, curPath + fileOrDirectory.getName() + "/");
                }
            }
        }
        if (null != in) {
            in.close();
        }
    }
}
